package com.hyq.learn.web.exception;


import com.hyq.learn.common.exception.InternalServiceException;
import com.hyq.learn.common.exception.SvcException;
import com.hyq.learn.common.exception.errorcode.CommonErrorCode;
import com.hyq.learn.common.response.CommonResponse;
import com.hyq.learn.web.exception.handler.AdviceTrait;
import com.hyq.learn.web.exception.handler.HttpAdviceTrait;
import com.hyq.learn.web.util.ServletRequestUtil;
import io.micrometer.core.instrument.util.IOUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.NativeWebRequest;


import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * 统一异常处理
 */
@ControllerAdvice
@Order(value = Ordered.LOWEST_PRECEDENCE - 100)
public class ExceptionTranslator implements AdviceTrait, HttpAdviceTrait {

    private final ThreadPoolTaskExecutor cmmAlarmTaskExecutor;

    public ExceptionTranslator(ThreadPoolTaskExecutor cmmAlarmTaskExecutor) {
        this.cmmAlarmTaskExecutor = cmmAlarmTaskExecutor;
    }


    @ExceptionHandler
    public ResponseEntity<CommonResponse<Void>> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, NativeWebRequest request) {
        String errorMessage = getErrorMessage(ex.getBindingResult());
        return create(new SvcException(CommonErrorCode.CONSTRAIN_VIOLATION, errorMessage, ex), request);
    }

    /**
     * 处理未知异常。
     *
     * @param ex               异常对象
     * @param nativeWebRequest 当前请求信息对象
     * @return 未知异常响应结果
     */
    @ExceptionHandler
    public ResponseEntity<CommonResponse<Void>> handleThrowable(Throwable ex, NativeWebRequest nativeWebRequest) {
        if (ex instanceof SvcException) {
            return create((SvcException) ex, nativeWebRequest);
        } else if (ex.getCause() instanceof SvcException) {
            return create((SvcException) ex.getCause(), nativeWebRequest);
        }
        HttpServletRequest httpServletRequest = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        cmmAlarmTaskExecutor.execute(() -> alarm(ex, httpServletRequest));
        boolean intranet = ServletRequestUtil.isIntranet(httpServletRequest);
        return create(ex, nativeWebRequest, intranet);
    }

    /**
     * 处理内部业务异常。
     *
     * @param ex      异常对象
     * @param request 当前请求信息对象
     * @return 内部业务异常响应结果
     */
    @ExceptionHandler
    public ResponseEntity<CommonResponse<Void>> handleSvcException(SvcException ex, NativeWebRequest request) {
        return create(ex, request);
    }

    @ExceptionHandler
    public ResponseEntity<CommonResponse<Void>> handleSvcException(InternalServiceException ex, NativeWebRequest request) {
        SvcException svcException = new SvcException(CommonErrorCode.INTERNAL_ERROR, ex.getMessage());
        cmmAlarmTaskExecutor.execute(() -> alarm(svcException, request.getNativeRequest(HttpServletRequest.class)));
        return create(svcException, request);
    }

    private String getErrorMessage(BindingResult ex) {
        List<String> errorMessages = new ArrayList<>();
        List<String> fieldErrorMessage = ex.getFieldErrors().stream().map(error -> error.getField() + " " + error.getDefaultMessage()).collect(Collectors.toList());
        List<String> objectErrorMessage = ex.getGlobalErrors().stream().map(error -> error.getObjectName() + " " + error.getDefaultMessage()).collect(Collectors.toList());
        errorMessages.addAll(fieldErrorMessage);
        errorMessages.addAll(objectErrorMessage);

        return String.join(";", errorMessages);
    }

    private void alarm(Throwable ex, HttpServletRequest servletRequest) {
        String url = getRequestUrl(servletRequest);
        Map<String, Object> params = new LinkedHashMap<>(20);
        params.put("request_url", url);
        params.put("request_method", servletRequest.getMethod());
        try {
            String requestBody = IOUtils.toString(servletRequest.getInputStream(), Charset.defaultCharset());
            if (StringUtils.isNotBlank(requestBody)) {
                params.put("request_body", requestBody);
            }
        } catch (Exception ignored) {
        }
        if (servletRequest.getParameterMap() != null && !servletRequest.getParameterMap().isEmpty()) {
            params.put("===== request_param start ", "====");
            params.putAll(servletRequest.getParameterMap());
            params.put("===== request_param end ", "====");
        }
    }

}
